package org.elsfs.auth.mvc;

import java.security.Principal;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.elsfs.auth.config.AuthorizationConfig;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 自定义 授权页面
 *
 * @author zeng
 * @since 0.0.1
 */
@Controller
public class AuthorizationConsentController {

  /** 注册的客户端 */
  private final RegisteredClientRepository registeredClientRepository;

  /** OAuth2授权同意服务 */
  private final OAuth2AuthorizationConsentService authorizationConsentService;

  /** 国际化 */
  private final ReloadableResourceBundleMessageSource messageSource;

  /** 默认构造器 */
  public AuthorizationConsentController(
      RegisteredClientRepository registeredClientRepository,
      OAuth2AuthorizationConsentService authorizationConsentService) {
    this.registeredClientRepository = registeredClientRepository;
    this.authorizationConsentService = authorizationConsentService;
    this.messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.addBasenames("classpath:i18n/consent");
    messageSource.setDefaultLocale(Locale.CHINA);
  }

  /**
   * 自定义sqty页面
   *
   * @param principal 认证的主体
   * @param model model
   * @param clientId 客户端id
   * @param scope 授权范围
   * @param state state验证值
   * @return 页面路径
   */
  @GetMapping(value = AuthorizationConfig.CUSTOM_CONSENT_PAGE_URI)
  public String consent(
      Principal principal,
      Model model,
      @RequestParam(OAuth2ParameterNames.CLIENT_ID) String clientId,
      @RequestParam(OAuth2ParameterNames.SCOPE) String scope,
      @RequestParam(OAuth2ParameterNames.STATE) String state) {

    // 删除已批准的作用域
    // 要批准的范围
    Set<String> scopesToApprove = new HashSet<>();
    // 之前批准的范围
    Set<String> previouslyApprovedScopes = new HashSet<>();
    // 获取客户端信息
    RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);
    // 查询 OAuth2授权同意
    OAuth2AuthorizationConsent currentAuthorizationConsent =
        this.authorizationConsentService.findById(registeredClient.getId(), principal.getName());
    // 查询 授权过的 授权范围
    Set<String> authorizedScopes;
    if (currentAuthorizationConsent != null) {
      authorizedScopes = currentAuthorizationConsent.getScopes();
    } else {
      authorizedScopes = Collections.emptySet();
    }
    for (String requestedScope : StringUtils.delimitedListToStringArray(scope, " ")) {
      // 去掉 openId
      if (OidcScopes.OPENID.equals(requestedScope)) {
        continue;
      }
      if (authorizedScopes.contains(requestedScope)) {
        // 添加之前授权的 scope
        previouslyApprovedScopes.add(requestedScope);
      } else {
        scopesToApprove.add(requestedScope);
      }
    }
    // 把参与的对象添加到 model
    model.addAttribute("clientId", clientId);
    model.addAttribute("state", state);
    model.addAttribute("scopes", withDescription(scopesToApprove));
    model.addAttribute("previouslyApprovedScopes", withDescription(previouslyApprovedScopes));
    model.addAttribute("principalName", principal.getName());
    model.addAttribute("messageSource", messageSource);

    return "consent";
  }

  private static Set<ScopeWithDescription> withDescription(Set<String> scopes) {
    Set<ScopeWithDescription> scopeWithDescriptions = new HashSet<>();
    for (String scope : scopes) {
      scopeWithDescriptions.add(new ScopeWithDescription(scope));
    }
    return scopeWithDescriptions;
  }
}
